
Anh Tuan
Data Science Expert

Trong trích xuất dữ liệu web, Maxun đang thu hút sự chú ý như một nền tảng mở mã nguồn, không cần lập trình, giúp các nhóm thu thập dữ liệu từ web dễ dàng hơn. Các quy trình dựa trên robot và SDK của nó cho phép cả lập trình viên và người dùng không chuyên xây dựng và duy trì các pipeline trích xuất dữ liệu mà không cần nỗ lực kỹ thuật lớn.
Tuy nhiên, nhiều trang web thực tế được bảo vệ bởi CAPTCHA, thường trở thành rào cản chính trong quá trình trích xuất dữ liệu. CapSolver hoạt động tốt cùng Maxun bằng cách xử lý các thách thức này ở cấp độ cơ sở hạ tầng. Với CapSolver, các robot Maxun có thể tiếp tục hoạt động trên các trang web có CAPTCHA một cách tin cậy hơn, kết hợp sự dễ sử dụng với khả năng trích xuất thực tế, sẵn sàng cho sản xuất.
Maxun là một nền tảng trích xuất dữ liệu web mở mã nguồn, không cần lập trình, cho phép người dùng huấn luyện robot để trích xuất trang web mà không cần viết mã. Nó có giao diện trực quan để xây dựng robot, SDK mạnh mẽ để kiểm soát robot theo chương trình, và hỗ trợ triển khai trên đám mây và tự lưu trữ.
| Lớp | Mô tả |
|---|---|
| Trích xuất | Xây dựng các quy trình trích xuất dữ liệu có cấu trúc với LLM hoặc các lựa chọn CSS |
| Trích xuất trang web | Chuyển trang web thành Markdown, HTML hoặc hình ảnh chụp màn hình sạch sẽ |
| Duyệt trang web | Tự động phát hiện và trích xuất nhiều trang bằng sitemap và liên kết |
| Tìm kiếm | Thực hiện tìm kiếm web và trích xuất nội dung từ kết quả (DuckDuckGo) |
Maxun cầu nối khoảng cách giữa sự đơn giản không cần lập trình và sự linh hoạt cho lập trình viên:
CapSolver là một dịch vụ giải CAPTCHA hàng đầu cung cấp các giải pháp dựa trên AI để vượt qua các thách thức CAPTCHA khác nhau. Với sự hỗ trợ cho nhiều loại CAPTCHA và thời gian phản hồi nhanh chóng, CapSolver tích hợp dễ dàng vào các quy trình tự động hóa.
CapSolver giúp các quy trình tự động hóa xử lý hầu hết các CAPTCHA và cơ chế xác minh chính thống thường gặp trong trích xuất web và tự động hóa trình duyệt, bao gồm:
Khi xây dựng robot Maxun tương tác với các trang web được bảo vệ—dù là trích xuất dữ liệu, theo dõi giá, hay nghiên cứu thị trường—các thách thức CAPTCHA trở thành rào cản lớn. Dưới đây là lý do tại sao tích hợp quan trọng:

# Cài đặt SDK Maxun
npm install maxun-sdk
# Cài đặt các phụ thuộc bổ sung cho tích hợp CapSolver
npm install axios
# Sao chép kho lưu trữ Maxun
git clone https://github.com/getmaxun/maxun.git
cd maxun
# Chạy bằng Docker Compose
docker-compose up -d
Tạo một tệp .env với cấu hình của bạn:
CAPSOLVER_API_KEY=your_capsolver_api_key
MAXUN_API_KEY=your_maxun_api_key
# Đối với Maxun Cloud (app.maxun.dev)
MAXUN_BASE_URL=https://app.maxun.dev/api/sdk
# Đối với Maxun tự lưu trữ (mặc định)
# MAXUN_BASE_URL=http://localhost:8080/api/sdk
Lưu ý: Cấu hình baseUrl là bắt buộc khi sử dụng Maxun Cloud. Các cài đặt tự lưu trữ mặc định là
http://localhost:8080/api/sdk.
Dưới đây là một dịch vụ TypeScript có thể tái sử dụng tích hợp CapSolver với Maxun:
import axios, { AxiosInstance } from 'axios';
interface TaskResult {
gRecaptchaResponse?: string;
token?: string;
cookies?: Array<{ name: string; value: string }>;
userAgent?: string;
}
interface CapSolverConfig {
apiKey: string;
timeout?: number;
maxAttempts?: number;
}
class CapSolverService {
private client: AxiosInstance;
private apiKey: string;
private maxAttempts: number;
constructor(config: CapSolverConfig) {
this.apiKey = config.apiKey;
this.maxAttempts = config.maxAttempts || 60;
this.client = axios.create({
baseURL: 'https://api.capsolver.com',
timeout: config.timeout || 30000,
headers: { 'Content-Type': 'application/json' },
});
}
private async createTask(taskData: Record<string, unknown>): Promise<string> {
const response = await this.client.post('/createTask', {
clientKey: this.apiKey,
task: taskData,
});
if (response.data.errorId !== 0) {
throw new Error(`Lỗi CapSolver: ${response.data.errorDescription}`);
}
return response.data.taskId;
}
private async getTaskResult(taskId: string): Promise<TaskResult> {
for (let attempt = 0; attempt < this.maxAttempts; attempt++) {
await this.delay(2000);
const response = await this.client.post('/getTaskResult', {
clientKey: this.apiKey,
taskId,
});
const { status, solution, errorDescription } = response.data;
if (status === 'ready') {
return solution as TaskResult;
}
if (status === 'failed') {
throw new Error(`Nhiệm vụ thất bại: ${errorDescription}`);
}
}
throw new Error('Hết thời gian chờ giải CAPTCHA');
}
private delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async solveReCaptchaV2(websiteUrl: string, websiteKey: string): Promise<string> {
const taskId = await this.createTask({
type: 'ReCaptchaV2TaskProxyLess',
websiteURL: websiteUrl,
websiteKey,
});
const solution = await this.getTaskResult(taskId);
return solution.gRecaptchaResponse || '';
}
async solveReCaptchaV3(
websiteUrl: string,
websiteKey: string,
pageAction: string = 'submit'
): Promise<string> {
const taskId = await this.createTask({
type: 'ReCaptchaV3TaskProxyLess',
websiteURL: websiteUrl,
websiteKey,
pageAction,
});
const solution = await this.getTaskResult(taskId);
return solution.gRecaptchaResponse || '';
}
async solveTurnstile(
websiteUrl: string,
websiteKey: string,
action?: string,
cdata?: string
): Promise<string> {
const taskData: Record<string, unknown> = {
type: 'AntiTurnstileTaskProxyLess',
websiteURL: websiteUrl,
websiteKey,
};
// Thêm thông tin tùy chọn
if (action || cdata) {
taskData.metadata = {};
if (action) (taskData.metadata as Record<string, string>).action = action;
if (cdata) (taskData.metadata as Record<string, string>).cdata = cdata;
}
const taskId = await this.createTask(taskData);
const solution = await this.getTaskResult(taskId);
return solution.token || '';
}
async checkBalance(): Promise<number> {
const response = await this.client.post('/getBalance', {
clientKey: this.apiKey,
});
return response.data.balance || 0;
}
}
export { CapSolverService, CapSolverConfig, TaskResult };
★ Gợi ý ─────────────────────────────────────
Dịch vụ CapSolver sử dụng mẫu polling (getTaskResult) vì việc giải CAPTCHA là bất đồng bộ—API chấp nhận một nhiệm vụ, xử lý nó trên máy chủ của họ, và trả về kết quả khi sẵn sàng. Khoảng thời gian 2 giây giữa các lần kiểm tra cân bằng giữa tính phản hồi và giới hạn tần suất API.
─────────────────────────────────────────────────
Vì Maxun hoạt động ở cấp độ cao hơn so với tự động hóa trình duyệt, cách tích hợp tập trung vào việc giải CAPTCHA trước hoặc trong khi thực thi robot:
import { Trích xuất } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';
const capSolver = new CapSolverService({ apiKey: CAPSOLVER_API_KEY });
const extractor = new Trích xuất({
apiKey: MAXUN_API_KEY,
baseUrl: MAXUN_BASE_URL,
});
async function trích xuấtVớiReCaptchaV2() {
const targetUrl = 'https://example.com/trang-bảo-mật';
const reCAPTCHA_siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';
console.log('Giải reCAPTCHA v2...');
// Giải CAPTCHA trước tiên
const token = await capSolver.solveReCaptchaV2(targetUrl, reCAPTCHA_siteKey);
console.log('CAPTCHA đã được giải, tạo robot trích xuất...');
// Tạo robot bằng cách chuỗi phương thức
const robot = await extractor
.tạo('Trình trích xuất sản phẩm')
.điều hướng(targetUrl)
.nhập('#g-recaptcha-response', token)
.nhấn('nút[type="submit"]')
.chờ(2000)
.thu-thập-danh-sách({ selector: '.sản-phẩm' });
// Chạy robot
const kết_quả = await robot.chạy({ timeout: 30000 });
console.log('Trích xuất hoàn tất:', kết_quả.dữ_liệu);
return kết_quả.dữ_liệu;
}
trích xuấtVớiReCaptchaV2().catch(console.error);
import { Trích xuất } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';
const capSolver = new CapSolverService({ apiKey: CAPSOLVER_API_KEY });
const extractor = new Trích xuất({
apiKey: MAXUN_API_KEY,
baseUrl: MAXUN_BASE_URL,
});
async function trích xuấtVớiReCaptchaV3() {
const targetUrl = 'https://example.com/v3-bảo-mật';
const reCAPTCHA_siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDEF';
console.log('Giải reCAPTCHA v3 với điểm số cao...');
// Giải với hành động trang tùy chỉnh
const token = await capSolver.solveReCaptchaV3(
targetUrl,
reCAPTCHA_siteKey,
'submit' // hành động trang
);
console.log('Token đã được nhận với điểm số cao, tạo robot...');
// Tạo robot trích xuất bằng cách chuỗi phương thức
const robot = await extractor
.tạo('Trình trích xuất bảo vệ v3')
.điều hướng(targetUrl)
.nhập('input[name="g-recaptcha-response"]', token)
.nhấn('#nút-gửi')
.chờ(2000)
.thu-thập-text({ kết_quả: '.kết_quả' });
const kết_quả = await robot.chạy({ timeout: 30000 });
console.log('Dữ liệu trích xuất:', kết_quả.dữ_liệu);
return kết_quả.dữ_liệu;
}
trích xuấtVớiReCaptchaV3().catch(console.error);
import { Trích xuất } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';
const capSolver = new CapSolverService({ apiKey: CAPSOLVER_API_KEY });
const scraper = new Trích xuất({
apiKey: MAXUN_API_KEY,
baseUrl: MAXUN_BASE_URL,
});
async function trích xuấtVớiTurnstile() {
const targetUrl = 'https://example.com/turnstile-bảo-mật';
const turnstile_siteKey = '0x4xxxxxxxxxxxxxxxxxxxxxxxxxxxxGHI';
console.log('Giải Cloudflare Turnstile...');
// Giải với thông tin tùy chọn (action và cdata)
const token = await capSolver.solveTurnstile(
targetUrl,
turnstile_siteKey,
'đăng nhập', // hành động tùy chọn
'0000-1111-2222-3333-thông-tin-cdata' // thông tin cdata tùy chọn
);
console.log('Turnstile đã được giải, tạo robot trích xuất...');
// Tạo robot trích xuất - đối với Turnstile, chúng ta thường cần
// gửi token qua yêu cầu POST trước, sau đó trích xuất
const robot = await scraper.tạo('trình-trích-xuất-turnstile', targetUrl, {
định_dạng: ['markdown', 'html'],
});
const kết_quả = await robot.chạy({ timeout: 30000 });
console.log('Trích xuất hoàn tất');
console.log('Markdown:', kết_quả.dữ_liệu.markdown?.substring(0, 500));
return kết_quả.dữ_liệu;
}
trích xuấtVớiTurnstile().catch(console.error);
Lớp Trích xuất được sử dụng để trích xuất dữ liệu có cấu trúc từ các phần tử trang cụ thể. Nó hỗ trợ trích xuất được hỗ trợ bởi LLM (dùng lời nhắc ngôn ngữ tự nhiên) và trích xuất không LLM (dùng các lựa chọn CSS):
import { Trích xuất } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const extractor = new Trích xuất({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});
interface DữLiệuSảnPhẩm {
tên: string;
giá: string;
đánh_giá: string;
}
async function trích xuấtSảnPhẩmVớiCaptcha(): Promise<DữLiệuSảnPhẩm[]> {
const targetUrl = 'https://example.com/products';
const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';
// Giải CAPTCHA trước
const captchaToken = await capSolver.solveReCaptchaV2(targetUrl, siteKey);
console.log('CAPTCHA đã được giải, tạo robot trích xuất...');
// Tạo robot trích xuất bằng cách kế thừa phương thức
const robot = await extractor
.create('Trình trích xuất sản phẩm')
.navigate(targetUrl)
.type('#g-recaptcha-response', captchaToken)
.click('button[type="submit"]')
.wait(3000)
.captureList({
selector: '.product-card',
pagination: { type: 'clickNext', selector: '.next-page' },
maxItems: 50,
});
// Chạy trích xuất
const result = await robot.run({ timeout: 60000 });
return result.data.listData as ProductData[];
}
extractProductsWithCaptcha()
.then((products) => {
products.forEach((product) => {
console.log(`${product.name}: ${product.price}`);
});
})
.catch(console.error);
★ Insight ─────────────────────────────────────
Phương thức captureList trong lớp Extract của Maxun tự động phát hiện các trường trong các mục danh sách và xử lý phân trang. Khi bạn chỉ định loại phân trang (scrollDown, clickNext, hoặc clickLoadMore), robot sẽ tiếp tục trích xuất cho đến khi đạt giới hạn của bạn hoặc hết trang.
─────────────────────────────────────────────────
Lớp Scrape chuyển trang web thành HTML sạch, Markdown phù hợp với LLM, hoặc hình ảnh chụp màn hình:
import { Scrape } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
import axios from 'axios';
const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const scraper = new Scrape({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});
interface ScrapeResult {
url: string;
markdown?: string;
html?: string;
captchaSolved: boolean;
}
async function scrapeWithCaptchaHandling(): Promise<ScrapeResult[]> {
const urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3',
];
const results: ScrapeResult[] = [];
for (const url of urls) {
try {
// Đối với các trang bị CAPTCHA, giải trước và thiết lập phiên
const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC'; // Lấy động nếu cần
console.log(`Giải CAPTCHA cho ${url}...`);
const captchaToken = await capSolver.solveReCaptchaV2(url, siteKey);
// Gửi CAPTCHA để nhận cookie phiên
const verifyResponse = await axios.post(`${url}/verify`, {
'g-recaptcha-response': captchaToken,
});
// Tạo robot ghi chú cho trang đã xác thực
const robot = await scraper.create(`scraper-${Date.now()}`, url, {
formats: ['markdown', 'html'],
});
const result = await robot.run({ timeout: 30000 });
results.push({
url,
markdown: result.data.markdown,
html: result.data.html,
captchaSolved: true,
});
// Dọn dẹp - xóa robot sau khi sử dụng
await robot.delete();
} catch (error) {
console.error(`Không thể ghi chú ${url}:`, error);
results.push({ url, captchaSolved: false });
}
}
return results;
}
scrapeWithCaptchaHandling().then(console.log).catch(console.error);
Lớp Crawl tự động phát hiện và ghi chú nhiều trang bằng cách sử dụng sitemap và theo dõi liên kết:
import { Crawl } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
import axios from 'axios';
const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const crawler = new Crawl({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});
interface PageResult {
url: string;
title: string;
text: string;
wordCount: number;
}
async function crawlWithCaptchaProtection(): Promise<PageResult[]> {
const startUrl = 'https://example.com';
const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';
// Giải CAPTCHA cho miền trước
console.log('Giải CAPTCHA để truy cập miền...');
const captchaToken = await capSolver.solveReCaptchaV2(startUrl, siteKey);
// Gửi CAPTCHA để thiết lập phiên (tùy theo miền)
await axios.post(`${startUrl}/verify`, {
'g-recaptcha-response': captchaToken,
});
// Tạo robot ghi chú với cấu hình theo miền
const robot = await crawler.create('site-crawler', startUrl, {
mode: 'domain', // Phạm vi ghi chú: 'domain', 'subdomain', hoặc 'path'
limit: 50, // Số trang tối đa để ghi chú
maxDepth: 3, // Sâu tối đa để theo dõi liên kết
useSitemap: true, // Phân tích sitemap.xml để lấy URL
followLinks: true, // Trích xuất và theo dõi liên kết trang
includePaths: ['/blog/', '/docs/'], // Mẫu regex để bao gồm
excludePaths: ['/admin/', '/login/'], // Mẫu regex để loại trừ
respectRobots: true, // Tôn trọng robots.txt
});
// Chạy ghi chú
const result = await robot.run({ timeout: 120000 });
// Mỗi trang trong kết quả chứa: metadata, html, text, wordCount, links
return result.data.crawlData.map((page: any) => ({
url: page.metadata.url,
title: page.metadata.title,
text: page.text,
wordCount: page.wordCount,
}));
}
crawlWithCaptchaProtection()
.then((pages) => {
console.log(`Đã ghi chú ${pages.length} trang`);
pages.forEach((page) => {
console.log(`- ${page.title}: ${page.url} (${page.wordCount} từ)`);
});
})
.catch(console.error);
Đối với các trang yêu cầu CAPTCHA trước khi truy cập nội dung, sử dụng quy trình xác thực trước:
import axios from 'axios';
import { Extract } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const extractor = new Extract({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});
interface SessionCookies {
name: string;
value: string;
domain: string;
}
async function preAuthenticateWithCaptcha(
loginUrl: string,
siteKey: string
): Promise<SessionCookies[]> {
// Bước 1: Giải CAPTCHA
const captchaToken = await capSolver.solveReCaptchaV2(loginUrl, siteKey);
// Bước 2: Gửi token CAPTCHA để nhận cookie phiên
const response = await axios.post(
loginUrl,
{
'g-recaptcha-response': captchaToken,
},
{
withCredentials: true,
maxRedirects: 0,
validateStatus: (status) => status < 400,
}
);
// Bước 3: Trích xuất cookie từ phản hồi
const setCookies = response.headers['set-cookie'] || [];
const cookies: SessionCookies[] = setCookies.map((cookie: string) => {
const [nameValue] = cookie.split(';');
const [name, value] = nameValue.split('=');
return {
name: name.trim(),
value: value.trim(),
domain: new URL(loginUrl).hostname,
};
});
return cookies;
}
async function extractWithPreAuth() {
const loginUrl = 'https://example.com/verify';
const targetUrl = 'https://example.com/protected-data';
const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';
// Xác thực trước để nhận cookie phiên
const sessionCookies = await preAuthenticateWithCaptcha(loginUrl, siteKey);
console.log('Phiên đã thiết lập, tạo robot trích xuất...');
// Tạo robot trích xuất bằng cách kế thừa phương thức
// Lưu ý: Sử dụng setCookies() để truyền cookie phiên đã xác thực
const robot = await extractor
.create('Trình trích xuất đã xác thực')
.setCookies(sessionCookies)
.navigate(targetUrl)
.wait(2000)
.captureText({ content: '.protected-content' });
// Chạy trích xuất
const result = await robot.run({ timeout: 30000 });
return result.data;
}
extractWithPreAuth().then(console.log).catch(console.error);
★ Insight ─────────────────────────────────────
Mẫu xác thực trước tách biệt việc giải CAPTCHA khỏi việc trích xuất dữ liệu. Điều này đặc biệt hữu ích với Maxun vì nó hoạt động ở mức trừu tượng cao hơn—thay vì chèn token vào DOM, bạn thiết lập phiên đã xác thực trước, sau đó để robot của Maxun làm việc trong phiên đó.
─────────────────────────────────────────────────
Xử lý CAPTCHA trên nhiều phiên robot song song:
import { Scrape } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const scraper = new Scrape({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});
interface ExtractionJob {
url: string;
siteKey?: string;
}
interface ExtractionResult {
url: string;
data: { markdown?: string; html?: string };
captchaSolved: boolean;
duration: number;
}
async function processJob(job: ExtractionJob): Promise<ExtractionResult> {
const startTime = Date.now();
let captchaSolved = false;
// Giải CAPTCHA nếu có site key
if (job.siteKey) {
console.log(`Giải CAPTCHA cho ${job.url}...`);
const token = await capSolver.solveReCaptchaV2(job.url, job.siteKey);
captchaSolved = true;
// Token sẽ được gửi để thiết lập phiên trước khi ghi chú
}
// Tạo và chạy robot ghi chú
const robot = await scraper.create(`scraper-${Date.now()}`, job.url, {
formats: ['markdown', 'html'],
});
const result = await robot.run({ timeout: 30000 });
// Dọn dẹp robot sau khi sử dụng
await robot.delete();
return {
url: job.url,
data: {
markdown: result.data.markdown,
html: result.data.html,
},
captchaSolved,
duration: Date.now() - startTime,
};
}
async function runParallelExtractions(
jobs: ExtractionJob[],
concurrency: number = 5
): Promise<ExtractionResult[]> {
const results: ExtractionResult[] = [];
const chunks: ExtractionJob[][] = [];
// Chia công việc thành các khối để kiểm soát độ đồng thời
for (let i = 0; i < jobs.length; i += concurrency) {
chunks.push(jobs.slice(i, i + concurrency));
}
for (const chunk of chunks) {
const chunkResults = await Promise.all(chunk.map(processJob));
results.push(...chunkResults);
}
return results;
}
// Ví dụ sử dụng
const jobs: ExtractionJob[] = [
{ url: 'https://site1.com/data', siteKey: '6Lc...' },
{ url: 'https://site2.com/data' },
{ url: 'https://site3.com/data', siteKey: '6Lc...' },
// ... nhiều công việc hơn
];
runParallelExtractions(jobs, 5)
.then((results) => {
const solved = results.filter((r) => r.captchaSolved).length;
console.log(`Hoàn thành ${results.length} trích xuất, giải được ${solved} CAPTCHA`);
results.forEach((r) => {
console.log(`${r.url}: ${r.duration}ms`);
});
})
.catch(console.error);
async function solveWithRetry<T>(
solverFn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await solverFn();
} catch (error) {
lastError = error as Error;
console.warn(`Lần thử ${attempt + 1} thất bại: ${lastError.message}`);
// Tái thử theo cấp số nhân
await new Promise((resolve) =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
throw new Error(`Đã vượt quá số lần thử lại: ${lastError?.message}`);
}
// Sử dụng
const token = await solveWithRetry(() =>
capSolver.solveReCaptchaV2(url, siteKey)
);
async function ensureSufficientBalance(minBalance: number = 1.0): Promise<void> {
const balance = await capSolver.checkBalance();
if (balance < minBalance) {
throw new Error(
`Số dư CapSolver không đủ: $${balance.toFixed(2)}. Vui lòng nạp tiền.`
);
}
console.log(`Số dư CapSolver: $${balance.toFixed(2)}`);
}
// Kiểm tra số dư trước khi bắt đầu công việc trích xuất
await ensureSufficientBalance(5.0);
interface CachedToken {
token: string;
timestamp: number;
}
class TokenCache {
private cache = new Map<string, CachedToken>();
private ttlMs: number;
constructor(ttlSeconds: number = 90) {
this.ttlMs = ttlSeconds * 1000;
}
private getKey(domain: string, siteKey: string): string {
return `${domain}:${siteKey}`;
}
get(domain: string, siteKey: string): string | null {
const key = this.getKey(domain, siteKey);
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > this.ttlMs) {
this.cache.delete(key);
return null;
}
return cached.token;
}
set(domain: string, siteKey: string, token: string): void {
const key = this.getKey(domain, siteKey);
this.cache.set(key, { token, timestamp: Date.now() });
}
}
const tokenCache = new TokenCache(90);
async function getCachedOrSolve(
url: string,
siteKey: string
): Promise<string> {
const domain = new URL(url).hostname;
const cached = tokenCache.get(domain, siteKey);
if (cached) {
console.log('Sử dụng token đã lưu');
return cached;
}
const token = await capSolver.solveReCaptchaV2(url, siteKey);
tokenCache.set(domain, siteKey, token);
return token;
}
Maxun hỗ trợ cấu hình thông qua biến môi trường và tùy chọn SDK:
| Cài đặt | Mô tả | Mặc định |
|---|---|---|
MAXUN_API_KEY |
Mã API Maxun của bạn | - |
MAXUN_BASE_URL |
URL cơ sở API Maxun | http://localhost:8080/api/sdk (tự host) hoặc https://app.maxun.dev/api/sdk (đám mây) |
CAPSOLVER_API_KEY |
Mã API CapSolver của bạn | - |
CAPSOLVER_TIMEOUT |
Thời gian chờ yêu cầu tính bằng ms | 30000 |
CAPSOLVER_MAX_ATTEMPTS |
Số lần thử tối đa | 60 |
import { Extract, Scrape, Crawl, Search } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
// Đối với Maxun Cloud (app.maxun.dev)
const MAXUN_BASE_URL = 'https://app.maxun.dev/api/sdk';
// Đối với Maxun tự host (mặc định)
// const MAXUN_BASE_URL = 'http://localhost:8080/api/sdk';
// Cấu hình các module SDK Maxun
const extractor = new Extract({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: MAXUN_BASE_URL,
});
const scraper = new Scrape({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: MAXUN_BASE_URL,
});
const crawler = new Crawl({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: MAXUN_BASE_URL,
});
const searcher = new Search({
apiKey: process.env.MAXUN_API_KEY!,
baseUrl: MAXUN_BASE_URL,
});
// Cấu hình CapSolver
const capSolver = new CapSolverService({
Học kiến trúc gỡ mã web Rust có thể mở rộng với reqwest, scraper, gỡ mã bất đồng bộ, gỡ mã trình duyệt không đầu, xoay proxy và xử lý CAPTCHA tuân thủ.

Tự động hóa việc giải CAPTCHA với Nanobot và CapSolver. Sử dụng Playwright để giải reCAPTCHA và Cloudflare tự động.
